﻿using HW_CommunicationLib.Model;
using HW_CommunicationLib;
using HW_PlcLib.Base;
using HW_PlcLib.Enums;
using HW_PlcLib.Interface;
using HW_PlcLib.Model;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Net.Sockets;

namespace HW_PlcLib
{
    ///<summary>
    ///描述: 
    ///作者: 孙小同
    ///创建时间: 2024/6/10 22:24:26
    ///</summary>
    ///


    /* 
     * 本机 127.0.0.1;502
     * PLC 192.168.1.101:502
    * 读示例：46494E53 0000001A 00000002 00000000 80000200 01000034 00FF0101 82 0000 00 0002
    * 读回复(34个字节)：46 49 4E 53 00 00 00 1A 00 00 00 02 00 00 00 00 C0 00 02 00 34 00 00 01 00 FF 01 01 00 00  00 14 00 1E
    * 写示例
    * 发送：46494E53 0000001D 00000002 00000000 80000200 01000034 00FF0102 82 0002 00 0002 4148 0000
    * 接收 46 49 4E 53 00 00 00 16 00 00 00 02 00 00 00 00 C0 00 02 00 34 00 00 01 00 00 01 02 00 00  (30个字节)
    */
    public class KeyencePlc : PlcBase
    {
        private Net net;

        public override event Action<object, PlcEventArgs> OnMessage;
        public override event Action<object, PlcEventArgs> OnError;
        public override event Action<object, DataPackage> OnSend;
        public override event Action<object, DataPackage> OnReceived;
        public override void Initialize(Net net)
        {
            this.net = net;
            communication.Initiallize(IPAddress.Parse(net.IP), net.Port);
            Init();
        }

        private void Init()
        {
            ConnectedState = Enums.ConnectedState.连接中;
            OnMessage?.Invoke(this, new PlcEventArgs { Message = "开始连接" });
            bool connectResult = Connect();
            if (!connectResult)
            {
                for (int i = 0; i < 5; i++)
                {
                    connectResult = Connect();
                    if (connectResult)
                    {
                        break;
                    }
                    if (i == 4)
                    {
                        OnMessage?.Invoke(this, new PlcEventArgs { Message = "请重启设备" });
                        return;
                    }
                }
            }

            ConnectedState = Enums.ConnectedState.通讯正常;
        }

        private bool Connect()
        {
            bool result = false;
            ConnectionResult connectionResult = communication.Connect();
            if (connectionResult.Success)
            {
                OnMessage?.Invoke(this, new PlcEventArgs { Message = "连接成功" });
                ConnectedState = Enums.ConnectedState.连接成功;
                result = true;
            }
            else
            {
                OnMessage?.Invoke(this, new PlcEventArgs { Message = "连接失败" });
                ConnectedState = Enums.ConnectedState.连接失败;
                result = false;
            }
            return result;
        }

        public override IPlcResult Start(Action action = null)
        {
            IPlcResult plcResult = new PlcResult();
            if (ConnectedState == Enums.ConnectedState.通讯正常)
            {
                OnMessage?.Invoke(this, new PlcEventArgs { Message = "初始化完成" });

            }
            else
            {
                plcResult.Message = "初始化未完成";
                plcResult.Success = false;
                OnError?.Invoke(this, new PlcEventArgs { Message = plcResult.Message });
                return plcResult;
            }
            communication.OnReceived += Communication_OnReceived;//订阅接收数据事件
            communication.OnError += Communication_OnError;//订阅错误数据事件
            var result = communication.Start();//通讯层开始工作
            if (!result)
            {
                plcResult.Message = "通讯层开始失败";
                plcResult.Success = false;
                OnError?.Invoke(this, new PlcEventArgs { Message = plcResult.Message });
                return plcResult;
            }
            plcResult.Success = true;
            plcResult.Message = "PLC成功开始工作";
            OnMessage?.Invoke(this, new PlcEventArgs { Message = plcResult.Message });
            return plcResult;
        }

        //接收通讯层错误的回调函数
        private void Communication_OnError(object arg1, CommunicationArgsBase arg)
        {
            OnError?.Invoke(this, new PlcEventArgs { Exception = arg.Exception, Message = arg.Message });
        }

        //接收通讯层数据的回调函数
        private void Communication_OnReceived(object arg1, DataPackage package)
        {
            OnReceived?.Invoke(this, package);
        }



        private DataPackage Send_Message(string Mess, int size)
        {
            try
            {
                DataPackage package = new DataPackage();
                package.CreateTime = DateTime.Now;
                package.NeedLength = 8 * size;
                package.SendBuffer = Encoding.ASCII.GetBytes(Mess);
                package.SendTime = DateTime.Now;
                OnSend?.Invoke(this, package);
                package = communication.Send(package);
                package.ReceiveTime = DateTime.Now;
                var ByteRD = Encoding.ASCII.GetString(package.ReceiveBuffer);
                var RET = ByteRD.Split('\r')[0].ToString();
                package.Result = RET;
                return package;
            }
            catch (Exception e)
            {
                OnError?.Invoke(this, new PlcEventArgs {Exception = e, Message = e.Message});
                return new DataPackage();
            }

        }

        //对地址强制置位
        public string SET_Addr(string Addr)
        {
            string mess = "ST " + Addr + "\r";
            DataPackage return_Data = Send_Message(mess, 2);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            OnReceived?.Invoke(this, return_Data);
            return (string)return_Data.Result;
        }

        //对地址强制复位
        public string RES_Addr(string Addr)
        {
            string mess = "RS " + Addr + "\r";
            DataPackage return_Data = Send_Message(mess, 2);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            OnReceived?.Invoke(this, return_Data);
            return (string)return_Data.Result;
        }

        //读取一个位
        public string Read_Bit(string Addr)
        {
            string Sendmess = "RD " + Addr + "\r";
            DataPackage return_Data = Send_Message(Sendmess, 1);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            OnReceived?.Invoke(this, return_Data);
            return (string)return_Data.Result;
        }

        //对地址批量强制置位
        public string SET_Addr(string Addr, int num)
        {
            string Sendmess = "STS " + Addr + " " + num + "\r";
            DataPackage return_Data = Send_Message(Sendmess, 2);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            OnReceived?.Invoke(this, return_Data);
            return (string)return_Data.Result;
        }

        //对地址批量强制复位
        public string RES_Addr(string Addr, int num)
        {
            string Sendmess = "RSS " + Addr + " " + num + "\r";
            DataPackage return_Data = Send_Message(Sendmess, 2);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            OnReceived?.Invoke(this, return_Data);
            return (string)return_Data.Result;
        }

        //连续读取一个位
        public string[] Read_Bit(string Addr, int num)
        {
            string Sendmess = "RDS " + Addr + " " + num + "\r";
            DataPackage return_Data = Send_Message(Sendmess, num);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            string result = (string)return_Data.Result;
            return_Data.Result = result.Split(' ');
            OnReceived?.Invoke(this, return_Data);
            return result.Split(' ');
        }

        //读取一个字整形
        public uint Read_UInt16(string Addr)
        {
            string Sendmess = "RD " + Addr + ".U\r";
            DataPackage return_Data = Send_Message(Sendmess, 1);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            string result = (string)return_Data.Result;
            return_Data.Result = Convert.ToUInt16(result);
            OnReceived?.Invoke(this, return_Data);
            return Convert.ToUInt16(result);
        }

        //连续读取一个字整形
        public uint[] Read_UInt16(string Addr, int num)
        {
            uint[] arrint = new uint[num];
            string Sendmess = "RDS " + Addr + ".U " + num.ToString() + "\r";
            DataPackage return_Data = Send_Message(Sendmess, num);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            string result = (string)return_Data.Result;
            string[] arr = result.Split(' ');
            for (int _i = 0; _i < num; _i++)
            {
                arrint[_i] = Convert.ToUInt16(arr[_i]);
            }
            return_Data.Result = arrint;
            OnReceived?.Invoke(this, return_Data);
            return arrint;
        }

        //读取一个字带符号整形
        public int Read_Int16(string Addr)
        {
            string Sendmess = "RD " + Addr + ".S\r";
            DataPackage return_Data = Send_Message(Sendmess, 1);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            string result = (string)return_Data.Result;
            return_Data.Result = Convert.ToInt16(result);
            OnReceived?.Invoke(this, return_Data);
            return Convert.ToInt16(result);
        }

        //连续读取一个字带符号整形
        public int[] Read_Int16(string Addr, int num)
        {
            int[] arrint = new int[num];
            string Sendmess = "RDS " + Addr + ".S " + num.ToString() + "\r";
            DataPackage return_Data = Send_Message(Sendmess, num);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            string result = (string)return_Data.Result;
            string[] arr = result.Split(' ');

            for (int _i = 0; _i < num; _i++)
            {
                arrint[_i] = Convert.ToInt16(arr[_i]);
            }
            return_Data.Result = arrint;
            OnReceived?.Invoke(this, return_Data);
            return arrint;
        }

        //读取两个字整形
        public uint Read_UInt32(string Addr)
        {
            string Sendmess = "RD " + Addr + ".D\r";
            DataPackage return_Data = Send_Message(Sendmess, 2);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            string result = (string)return_Data.Result;
            return_Data.Result = Convert.ToUInt32(result);
            OnReceived?.Invoke(this, return_Data);
            return Convert.ToUInt32(result);
        }

        //连续读取两个字整形
        public uint[] Read_UInt32(string Addr, int num)
        {
            uint[] arrint = new uint[num];
            string Sendmess = "RDS " + Addr + ".D " + num.ToString() + "\r";
            DataPackage return_Data = Send_Message(Sendmess, num * 2);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            string result = (string)return_Data.Result;
            
            string[] arr = result.Split(' ');
            for (int _i = 0; _i < num; _i++)
            {
                arrint[_i] = Convert.ToUInt32(arr[_i]);
            }
            return_Data.Result = arrint;
            OnReceived?.Invoke(this, return_Data);
            return arrint;
        }

        //读取两个字带符号整形
        public int Read_Int32(string Addr)
        {
            string Sendmess = "RD " + Addr + ".L\r";
            DataPackage return_Data = Send_Message(Sendmess, 2);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            string result = (string)return_Data.Result;
            return_Data.Result = Convert.ToInt32(result);
            OnReceived?.Invoke(this, return_Data);
            return Convert.ToInt32(result);
        }

        //连续读取两个字带符号整形
        public int[] Read_Int32(string Addr, int num)
        {
            int[] arrint = new int[num];
            string Sendmess = "RDS " + Addr + ".L " + num.ToString() + "\r";
            DataPackage return_Data = Send_Message(Sendmess, num * 2);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            string result = (string)return_Data.Result;
            string[] arr = result.Split(' ');
            for (int _i = 0; _i < num; _i++)
            {
                arrint[_i] = Convert.ToInt32(arr[_i]);
            }
            return_Data.Result = arrint;
            OnReceived?.Invoke(this, return_Data);
            return arrint;
        }

        //读取单精度浮点
        public float Read_Float(string Addr)
        {
            string Sendmess = "RD " + Addr + ".L\r";
            DataPackage return_Data = Send_Message(Sendmess, 2);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            string result = (string)return_Data.Result;
            return_Data.Result = DecToFloat(result);
            OnReceived?.Invoke(this, return_Data);
            return DecToFloat(result);
        }

        //连续读取单精度浮点
        public float[] Read_Float(string Addr, int num)
        {
            float[] arrflo = new float[num];
            string Sendmess = "RDS " + Addr + ".L " + num.ToString() + "\r";
            DataPackage return_Data = Send_Message(Sendmess, num * 2);
            string result = PLCToDec((string)return_Data.Result);
            string[] arr = result.Split(' ');
            for (int _i = 0; _i < num; _i++)
            {
                arrflo[_i] = DecToFloat(arr[_i]);
            }
            return_Data.Result = arrflo;
            OnReceived?.Invoke(this, return_Data);
            return arrflo;
        }

        //读取字符串
        public string Read_String(string Addr)
        {
            string Sendmess = "RD " + Addr + ".U\r";
            DataPackage return_Data = Send_Message(Sendmess, 2);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            string result = (string)return_Data.Result;
            return_Data.Result = Get_Char(result);
            OnReceived?.Invoke(this, return_Data);
            return Get_Char(result);
        }

        //连续读取字符串
        public string Read_String(string Addr, int num)
        {
            string ret = string.Empty;
            string Sendmess = "RDS " + Addr + ".U " + num.ToString() + "\r";
            DataPackage return_Data = Send_Message(Sendmess, 2 * num);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            string result = (string)return_Data.Result;
            string[] arr = result.Split(' ');
            string _ = "";
            for (int _i = 0; _i < num; _i++)
            {
                _ = Get_Char(arr[_i]);
                ret += _;
                if (_.Length % 2 != 0 || _.Length == 0)
                {
                    break;
                }
            }
            return_Data.Result = ret;
            OnReceived?.Invoke(this, return_Data);
            return ret;
        }

        //写一个字整形
        public string Write_UInt16(string Addr, string Value)
        {
            string Sendmess = "WR " + Addr + ".U " + Value + "\r";
            DataPackage return_Data = Send_Message(Sendmess, 1);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            string result = (string)return_Data.Result;
            OnReceived?.Invoke(this, return_Data);
            return result;
        }
        //连续写一个字整形
        public string Write_UInt16(string Addr, string[] Value)
        {
            string mess = "";
            foreach (string me in Value)
            {
                mess += " " + me;
            }
            string Sendmess = "WRS " + Addr + ".U " + Value.Length + mess + "\r";
            DataPackage return_Data = Send_Message(Sendmess, 1);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            string result = (string)return_Data.Result;
            OnReceived?.Invoke(this, return_Data);
            return result;
        }

        //写一个字整形带符号
        public string Write_Int16(string Addr, string Value)
        {
            string Sendmess = "WR " + Addr + ".S " + Value + "\r";
            DataPackage return_Data = Send_Message(Sendmess, 1);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            string result = (string)return_Data.Result;
            OnReceived?.Invoke(this, return_Data);
            return result;
        }

        //连续写一个字整形带符号
        public string Write_Int16(string Addr, string[] Value)
        {
            string mess = "";
            foreach (string me in Value)
            {
                mess += " " + me;
            }
            string Sendmess = "WRS " + Addr + ".S " + Value.Length + mess + "\r";
            DataPackage return_Data = Send_Message(Sendmess, 1);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            string result = (string)return_Data.Result;
            OnReceived?.Invoke(this, return_Data);
            return result;
        }

        //写两个字整形
        public string Write_UInt32(string Addr, string Value)
        {
            string Sendmess = "WR " + Addr + ".D " + Value + "\r";
            DataPackage return_Data = Send_Message(Sendmess, 1);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            string result = (string)return_Data.Result;
            OnReceived?.Invoke(this, return_Data);
            return result;
        }
        //连续写两个字整形
        public string Write_UInt32(string Addr, string[] Value)
        {
            string mess = "";
            foreach (string me in Value)
            {
                mess += " " + me;
            }
            string Sendmess = "WRS " + Addr + ".D " + Value.Length + mess + "\r";
            DataPackage return_Data = Send_Message(Sendmess, 1);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            string result = (string)return_Data.Result;
            OnReceived?.Invoke(this, return_Data);
            return result;
        }

        //写两个字整形带符号
        public string Write_Int32(string Addr, string Value)
        {
            string Sendmess = "WR " + Addr + ".L " + Value + "\r";
            DataPackage return_Data = Send_Message(Sendmess, 1);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            string result = (string)return_Data.Result;
            OnReceived?.Invoke(this, return_Data);
            return result;
        }
        //连续写两个字整形带符号
        public string Write_Int32(string Addr, string[] Value)
        {
            string mess = "";
            foreach (string me in Value)
            {
                mess += " " + me;
            }
            string Sendmess = "WRS " + Addr + ".L " + Value.Length + mess + "\r";
            DataPackage return_Data = Send_Message(Sendmess, 1);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            string result = (string)return_Data.Result;
            OnReceived?.Invoke(this, return_Data);
            return result;
        }

        //写单精度浮点
        public string Write_Float(string Addr, string Value)
        {
            Value = FloToDec(Value);
            string Sendmess = "WR " + Addr + ".D " + Value + "\r";
            DataPackage return_Data = Send_Message(Sendmess, 1);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            string result = (string)return_Data.Result;
            OnReceived?.Invoke(this, return_Data);
            return result;
        }
        //连续写单精度浮点
        public string Write_Float(string Addr, string[] Value)
        {
            string mess = "";
            foreach (string me in Value)
            {
                mess += " " + FloToDec(me);
            }
            string Sendmess = "WRS " + Addr + ".D " + Value.Length + mess + "\r";
            DataPackage return_Data = Send_Message(Sendmess, 1);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            string result = (string)return_Data.Result;
            OnReceived?.Invoke(this, return_Data);
            return result;
        }
        //写字符串
        public string Write_String(string Addr, string Value)
        {
            string[] _ = Getstr(Value, 2);
            int _Len = _.Length;
            string va = "";
            for (int _i = 0; _i < _Len; _i++)
            {
                va += " " + StringGetDec(_[_i]);
            }
            string Sendmess = "WRS " + Addr + ".U " + _Len.ToString() + " " + va + "\r";
            DataPackage return_Data = Send_Message(Sendmess, _Len);
            return_Data.Result = PLCToDec((string)return_Data.Result);
            string result = (string)return_Data.Result;
            OnReceived?.Invoke(this, return_Data);
            return result;
        }

        //将PLC传回的数据转化为字符串表示的整数
        private string PLCToDec(string Return_Data)
        {
            try
            {
                if (Return_Data == "" || Return_Data == null)
                    Return_Data = "0";

                if (Return_Data == "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0")
                    Return_Data = "error";
                else
                    Return_Data = Return_Data.Split('\r')[0];

                return Return_Data;
            }
            catch (Exception e)
            {
                OnError?.Invoke(this, new PlcEventArgs { Exception = e, Message = e.Message });
                return null;
            }

        }

        private float DecToFloat(string Decimal)
        {
            int dec = Convert.ToInt32(Decimal);
            byte[] bt = BitConverter.GetBytes(dec);
            float flo = BitConverter.ToSingle(bt, 0);
            return flo;
        }
        //转换为double
        private double DecToDouble(string Decimal)
        {
            int dec = Convert.ToInt32(Decimal);
            byte[] bt = BitConverter.GetBytes(dec);
            double dou = BitConverter.ToDouble(bt, 0);
            return dou;
        }
        //flo转换为Dec
        private string FloToDec(string flo)
        {
            float dec = Convert.ToSingle(flo);
            byte[] bt = BitConverter.GetBytes(dec);
            string return_mess = BitConverter.ToUInt32(bt, 0).ToString();
            return return_mess;
        }

        //转换为char
        private string Get_Char(string dec)
        {
            int i = Convert.ToInt32(dec);
            byte[] bt = BitConverter.GetBytes(i);
            var bt0 = bt[0];
            var bt1 = bt[1];
            var c0 = ((char)bt0).ToString();
            if (c0 == "\0")
                c0 = "";
            var c1 = ((char)bt1).ToString();
            if (c1 == "\0")
            {
                c0 = "";
                c1 = "";
            }
            string c = c1 + c0;
            return c;
        }

        //转换为DEC
        private string StringGetDec(string str)
        {
            byte[] byte_1 = Encoding.Default.GetBytes(str);
            int Len = byte_1.Length;
            byte[] Middle_B = new byte[2];
            Middle_B[1] = byte_1[0];
            if (Len == 2)
                Middle_B[0] = byte_1[1];
            else if (Len == 1)
                Middle_B[0] = 0;
            string return_mess = BitConverter.ToInt16(Middle_B, 0).ToString();
            return return_mess;
        }

        //拆分字符串
        private string[] Getstr(string str, int len)
        {
            str += "\0";
            double i = str.Length;
            string[] myarray = new string[int.Parse(Math.Ceiling(i / len).ToString())];
            for (int j = 0; j < myarray.Length; j++)
            {
                len = len <= str.Length ? len : str.Length;
                myarray[j] = str.Substring(0, len);
                str = str.Substring(len, str.Length - len);
            }
            return myarray;
        }


        public override void Stop()
        {
            base.Stop();
            //todo 将来还有一些需要在Ormonplc中结束的操作
        }
   
        /// <summary>
        /// 写入数据包生命周期日志
        /// </summary>
        /// <param name="command"></param>
        /// <param name="package"></param>
        /// <param name="result"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void Writelog(DataPackage package)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append($"发送时间:{package.SendTime} | 回复时间:{package.ReceiveTime}\r");
            sb.Append($"发送:{package.SendBufferToString}");
            sb.Append($"接收:{package.ReceiveBufferToString}");
            Console.WriteLine(sb.ToString());
        }


        /// <summary>
        /// 写入数据包生命周期日志
        /// </summary>
        /// <param name="command"></param>
        /// <param name="package"></param>
        /// <param name="result"></param>
        /// <exception cref="NotImplementedException"></exception>
        private void Writelog<T>(DataPackage package, IPlcResult<T> result)
        {
            if (result == null) return;

            StringBuilder sb = new StringBuilder();
            sb.Append($"发送时间:{package.SendTime} | 回复时间:{package.ReceiveTime}\r");
            sb.Append($"发送:{package.SendBufferToString}\r");
            sb.Append($"接收:{package.ReceiveBufferToString}\r");
            Console.WriteLine(sb.ToString());
        }
    }
}
